#ifndef __M_HELPER_H__
#define __M_HELPER_H__

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cerrno>
#include <sqlite3.h>
#include <vector>
#include <random>
#include <iomanip>
#include <sstream>
#include <atomic>
#include <sys/stat.h>
#include "logger.hpp"

namespace mmq
{

    class SqliteHelper
    {
    private:
        typedef int(*SqliteCallback)(void*, int, char**, char**);
    public:
        SqliteHelper(const std::string &dbfile) : _dbfile(dbfile), _handler(nullptr)
        {}

        bool Open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);
            if(ret != SQLITE_OK)
            {
                ELOG("打开数据库失败: %s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }

        bool Exec(const std::string &sql, SqliteCallback cb, void *arg)
        {
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
            if(ret != SQLITE_OK)
            {
                ELOG("%s \n语句执行失败: %s", sql.c_str(), sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }

        void Close()
        {
            // int sqlite3_close_v2(sqlite3*); ⽆论如何都会返回SQLITE_OK
            if(_handler) sqlite3_close_v2(_handler);
        }

    private:
        std::string _dbfile;
        sqlite3 *_handler;
    };

    class StrHelper
    {
    public:
        // str为要分割的字符串，sep为分隔符， result为分割后字符串存储的地方，返回给用户
        static int Split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
        {
            // idx为开始查找的位置，pos为查找后目标位置
            size_t idx = 0, pos = 0;
            while(idx < str.size())
            {
                pos = str.find(sep, idx);
                if(pos == std::string::npos)
                {
                    result.push_back(str.substr(idx));
                    return result.size();
                }
                // 说明查找位置起始就是分隔符
                if(pos == idx)
                {
                    idx = pos + sep.size();
                    continue;
                }
                result.push_back(str.substr(idx, pos - idx));
                idx = pos + sep.size();
            }
            return result.size();
        }
    };

    class UuidHelper
    {
    public:
        static std::string uuid()
        {
            std::random_device rd;
            std::mt19937_64 generator(rd());                            // 通过梅森旋转算法，生成一个伪随机数
            std::uniform_int_distribution<size_t> distribution(0, 255); // 限定随机数范围[0, 255]
            std::stringstream ss;
            for(int i = 0; i < 8; i++) // 添加8个2位16进制[0, 255]随机数，拼接得到16位字符串
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
                if(i == 3 || i == 5 || i == 7)
                    ss << "-";
            }
            static std::atomic<size_t> sep(1); // 设置为static，原子类型整数，初始化为1
            size_t num = sep.fetch_add(1);     // 类似于后置++
            for(int i = 7; i >= 0; i--)        // 64位无符号整型，分8次分别以8位为单位从高位到低位处理
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xff);
                if(i == 6) ss << "-";
            }
            return ss.str();
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string &filename) : _filename(filename)
        {}

        // 文件是否存在
        bool Exists()
        {
            struct stat st;
            return (stat(_filename.c_str(), &st) == 0);
        }

        // 文件大小
        size_t Size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if(ret < 0) return 0;
            return st.st_size;
        }

        // 读取文件内容
        bool Read(char *body, size_t offset, size_t len)
        {
            // 1. 打开文件
            // 使用binary二进制方式打开是因为默认文本打开可能会对换行进行一些\r\n处理
            std::ifstream ifs(_filename.c_str(), std::ios::binary | std::ios::in);
            if(ifs.is_open() == false)
            {
                ELOG("%s 文件打开失败！", _filename.c_str());
                return false;
            }
            // 2. 跳转文件读写位置
            ifs.seekg(offset, std::ios::beg); // 从begin跳转到之后的offset位置，对输入流定位
            // 3. 读取文件数据
            ifs.read(body, len);              // 从读写位置读取len字节，放到body中
            if(ifs.good() == false)           // 上一步出错即读取失败
            {
                ELOG("%s 文件读取失败！", _filename.c_str());
                ifs.close();
                return false;
            }
            // 4. 关闭文件
            ifs.close();
            return true;
        }   

        bool Read(std::string &body)
        {
            size_t fsize = Size();           // 根据文件大小，调整body大小
            body.resize(fsize);              // 因为传入的是char*，需要现在这里进行扩容
            return Read(&body[0], 0, fsize);
        }

        // 向文件写入
        bool Write(const char *body, size_t offset, size_t len)
        {
            // 1. 打开文件
            // 不使用ofstream是因为它没有seekg跳转读写位置的句柄
            std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
            if(fs.is_open() == false)
            {
                ELOG("%s 文件打开失败！", _filename.c_str());
                return false;
            }
            // 2. 跳转到读写位置
            fs.seekp(offset, std::ios::beg); // 对输出流进行定位
            // 3. 写入数据
            fs.write(body, len);
            if(fs.good() == false)
            {
                ELOG("%s 文件写入失败！", _filename.c_str());
                fs.close();
                return false;
            }
            // 3. 关闭文件
            fs.close();
            return true;
        }

        bool Write(const std::string &body)
        {
            // 注意，若写入之前存在100字节数据，写入50字节数据。之后文件大小仍为100字节，只是前50字节被覆盖了
            return Write(body.c_str(), 0, body.size());
        }

        // 重命名
        bool Rename(const std::string &nname)
        {
            return (::rename(_filename.c_str(), nname.c_str()) == 0);
        }

        // 创建文件
        static bool CreateFile(const std::string &filename)
        {
            std::ofstream ofs(filename, std::ios::binary | std::ios::out);
            if(ofs.is_open() == false)
            {
                ELOG("%s 文件创建失败！", filename.c_str());
                return false;
            }
            ofs.close();
            return true;
        }

        // 删除文件
        static bool RemoveFile(const std::string &filename)
        {
            return (::remove(filename.c_str()) == 0);
        }

        // 创建目录
        static bool CreateDirectory(const std::string &path)
        {
            // aaa/bbb/ccc/ddd要迭代创建，从第一个目录开始创建
            size_t idx = 0, pos = 0;
            while(idx < path.size())
            {
                pos = path.find('/', idx);
                if(pos == std::string::npos)
                {
                    // 最后一个目录，直接mkdir整个path
                    return (mkdir(path.c_str(), 0775) == 0); 
                }
                std::string subpath = path.substr(0, pos); // 注意是从0开始
                int ret = mkdir(subpath.c_str(), 0775);
                if(ret != 0 && errno != EEXIST)            // 跳过创建过的目录
                {
                    ELOG("创建目录 %s 失败! err: %s", path.c_str(), strerror(errno));
                    return false;
                }
                idx = pos + 1;
            }
            return true;
        }

        // 删除目录
        static bool RemoveDirectory(const std::string &path)
        {
            // rm -rf 调用系统指令进行删除 system()
            std::string cmd = "rm -rf " + path;
            return (system(cmd.c_str()) != -1);
        }

        // 获取上级目录路径
        static std::string ParentDirectory(const std::string &filename)
        {
            size_t pos = filename.find_last_of("/");
            if(pos == std::string::npos)
            {
                return "./";
            }
            return filename.substr(0, pos);
        }

    private:
        std::string _filename;
    };

}
#endif